package graphics.usb;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;

import graphics.base.Data3D;
import graphics.base.Point3D;
import graphics.base.Rectangular;
import graphics.base.Triangle;

/**
 * @author ephuizi@gmail.com
 */
public class StringConvert3D {
	private static final float USB_LENGTH = 16f;
	private static final float USB_WIDTH = 16f;
	private static final float USB_HEIGHT = 8f;

	private static final float USB_H_LENGTH = 12.8f;// 内槽长
	private static final float USB_H_WIDTH = 10f;// 内槽宽
	private static final float USB_H_HEIGHT = 4f;// 内槽高

	private static final int SIDE_WIDTH = 100;

	private static final float INSIDE_THICKNESS_RATE = 0.3f;// 夹层的厚度

	/**
	 * 图片转3d,并将外框最低点模型移到原点
	 * 
	 * @param grayImg
	 * @return
	 */
	private static Data3D convert3D0(BufferedImage grayImg) {
		List<Triangle> list = new ArrayList<>();
		Data3D data3D = new Data3D(list);
		int intImageWidth = grayImg.getWidth();
		int intImageHeight = grayImg.getHeight();

		int intHeights[][] = new int[intImageWidth + 2][intImageHeight + 2];
		// 用于减少重复计算的点
		byte bitTopDone[][] = new byte[intImageWidth + 2][intImageHeight + 2];

		// 为了模型封闭,矩阵放宽,所以横列+2
		// calculate iniMinDepth
		int white = Color.WHITE.getRGB();
		// 计算出最大高度
		// 而二值化的图片
		int calculateHMax = (int) (USB_HEIGHT * (1 - INSIDE_THICKNESS_RATE) / 2);
		for (int x = 0; x < intImageWidth; x++) {
			for (int y = 0; y < intImageHeight; y++) {
				intHeights[x + 1][intImageHeight - y] = grayImg.getRGB(x, y) == white ? 0 : calculateHMax;
			}
		}
		// 计算
		// 利用color计算高度
		for (int x = 0; x < intImageWidth + 1; x++) {
			for (int y = 0; y < intImageHeight + 1; y++) {
				// 这个点还没被计算
				if (bitTopDone[x][y] == 0) {
					// 为了减少计算量,排除没高度的区域
					if (intHeights[x][y] > 0 || intHeights[x + 1][y] > 0 || intHeights[x][y + 1] > 0 || intHeights[x + 1][y + 1] > 0) {
						int intBlockSize = 0;

						boolean bitNoMatch = false;
						// (x,y)点之后的图片最长边
						int intMatchSize = 0;
						int intTemp1 = intImageWidth - x;
						int intTemp2 = intImageHeight - y;
						if (intTemp1 > intTemp2) {
							intMatchSize = intTemp1;
						} else {
							intMatchSize = intTemp2;
						}

						for (int i = 1; i < intMatchSize + 1; i++) {
							for (int j = 0; j < intMatchSize + 1; j++) {
								if (intHeights[x][y] != intHeights[x + i][y + j] || intHeights[x][y] != intHeights[x + j][y + i]) {
									bitNoMatch = true;// 与前面正方形区域高度不一致
									break;
								}
							}
							if (bitNoMatch)
								break;
							else
								intBlockSize = i - 1;// 记录高度一致的正方形大小
						}

						for (int i = 1; i < intBlockSize; i++) {
							for (int j = 0; j < intBlockSize; j++) {
								bitTopDone[x + i][y + j] = 1;// 记录被计算的正方形区域
							}
						}
						intBlockSize += 1;// 为了模型是密封的,边的高度与区域的高度不一样

						Triangle objTriangle1 = new Triangle();
						objTriangle1.setFirstPoint(x, y, intHeights[x][y]);
						objTriangle1.setSecodePoint(x + intBlockSize, y + intBlockSize, intHeights[x + intBlockSize][y + intBlockSize]);
						objTriangle1.setThridPoint(x, y + intBlockSize, intHeights[x][y + intBlockSize]);

						Triangle objTriangle2 = new Triangle();
						objTriangle2.setFirstPoint(x, y, intHeights[x][y]);
						objTriangle2.setSecodePoint(x + intBlockSize, y, intHeights[x + intBlockSize][y]);
						objTriangle2.setThridPoint(x + intBlockSize, y + intBlockSize, intHeights[x + intBlockSize][y + intBlockSize]);

						list.add(objTriangle1);
						list.add(objTriangle2);
					}
				}
			}
		}

		//
		// 补全(x == intImageWidth && y == 1)
		//
		if (intHeights[intImageWidth][1] > 0) {
			Triangle objTriangle3 = new Triangle();
			objTriangle3.setFirstPoint(intImageWidth, 1, intHeights[intImageWidth][1]);
			objTriangle3.setSecodePoint(intImageWidth + 1, 0, intHeights[intImageWidth + 1][0]);
			objTriangle3.setThridPoint(intImageWidth, 0, intHeights[intImageWidth][0]);

			Triangle objTriangle4 = new Triangle();
			objTriangle4.setFirstPoint(intImageWidth, 1, intHeights[intImageWidth][1]);
			objTriangle4.setSecodePoint(intImageWidth + 1, 0, intHeights[intImageWidth + 1][0]);
			objTriangle4.setThridPoint(intImageWidth + 1, 1, intHeights[intImageWidth + 1][1]);

			list.add(objTriangle3);
			list.add(objTriangle4);
		}

		//
		// 补全(x == 1 && y == intImageHeight)
		//
		if (intHeights[1][intImageHeight] > 0) {
			Triangle objTriangle5 = new Triangle();
			objTriangle5.setFirstPoint(1, intImageHeight, intHeights[1][intImageHeight]);
			objTriangle5.setSecodePoint(0, intImageHeight + 1, intHeights[0][intImageHeight + 1]);
			objTriangle5.setThridPoint(0, intImageHeight, intHeights[0][intImageHeight]);

			Triangle objTriangle6 = new Triangle();
			objTriangle6.setFirstPoint(1, intImageHeight, intHeights[1][intImageHeight]);
			objTriangle6.setSecodePoint(0, intImageHeight + 1, intHeights[0][intImageHeight + 1]);
			objTriangle6.setThridPoint(1, intImageHeight + 1, intHeights[1][intImageHeight + 1]);

			list.add(objTriangle5);
			list.add(objTriangle6);
		}
		data3D.calculateMaxAndMin();
		// add bottom
		float bottomZ = -USB_HEIGHT / 2 + calculateHMax;// 减少使用float,防止精度丢失
		Point3D maxMidZ = new Point3D(data3D.max.x, data3D.max.y, 0);
		Point3D minMidZ = new Point3D(data3D.min.x, data3D.min.y, bottomZ);
		List<Triangle> cubeMid = cuboid(minMidZ, maxMidZ);
		list.addAll(cubeMid);
		// 增加反面
		int positiveSize = list.size();
		for (int i = 0; i < positiveSize; i++) {
			Triangle positive = list.get(i);
			Triangle reverse = new Triangle();
			reverse.setFirstPoint(positive.x1, positive.y1, 2 * bottomZ - positive.z1);
			reverse.setSecodePoint(positive.x2, positive.y2, 2 * bottomZ - positive.z2);
			reverse.setThridPoint(positive.x3, positive.y3, 2 * bottomZ - positive.z3);

			list.add(reverse);
		}
		data3D.calculateMaxAndMin();
		// 移动到原点
		Point3D org = new Point3D(0, 0, 0);
		if (!data3D.min.equals(org)) {
			for (int i = 0; i < list.size(); i++) {
				Triangle tri = list.get(i);
				tri.toMoveX(-data3D.min.x);
				tri.toMoveY(-data3D.min.y);
				tri.toMoveZ(-data3D.min.z);
			}
		}
		data3D.calculateMaxAndMin();
		return data3D;
	}

	/**
	 * 将单个字符3D化
	 *
	 * @param img2D
	 * @return
	 */
	private static Data3D convert3D(char c, Font font, int fontSize, Color color, float scale) {

		int imgWidth = fontSize;
		BufferedImage grayImg = new BufferedImage(imgWidth, SIDE_WIDTH, BufferedImage.TYPE_BYTE_BINARY);
		for (int x = 0; x < grayImg.getWidth(); x++) {
			for (int y = 0; y < grayImg.getHeight(); y++) {
				grayImg.setRGB(x, y, Color.WHITE.getRGB());
			}
		}
		Graphics graphics = grayImg.getGraphics();
		// int strWidth = graphics.getFontMetrics().stringWidth("好");
		int baseline = SIDE_WIDTH / 6;
		graphics.setFont(font);
		graphics.setColor(color);
		graphics.drawString(String.valueOf(c), 0, SIDE_WIDTH - baseline);
		graphics.dispose();

		Data3D data3D = convert3D0(grayImg);
		formalImg3D(data3D, scale);
		return data3D;
	}

	/**
	 * 调整模型在xy平面的大小,以适合usb头
	 * 
	 * @param data3D
	 * @param scale
	 */
	private static void formalImg3D(Data3D data3D, float scale) {
		List<Triangle> list = data3D.tris;
		// y变化率
		float scaleRateY = USB_LENGTH * scale / (data3D.max.y - data3D.min.y);
		for (int i = 0; i < list.size(); i++) {
			Triangle tri = list.get(i);
			tri.toScaleX(scaleRateY);
			tri.toScaleY(scaleRateY);
		}
		data3D.calculateMaxAndMin();
	}

	public static Data3D convert3DDifBottom(List<DrawStringParam> params) {
		Data3D data3D = null;
		for (int i = 0; i < params.size(); i++) {
			DrawStringParam param = params.get(i);
			int fontSize = (int) (SIDE_WIDTH * param.scale);
			Font font = new Font(param.fontName, param.fontStyle, fontSize);
			for (int j = 0; j < param.target.length(); j++) {
				Data3D char3D = convert3D(param.target.charAt(j), font, fontSize, param.color, param.scale);
				if (data3D == null)
					data3D = char3D;
				else {
					for (int k = 0; k < char3D.tris.size(); k++) {
						Triangle tri = char3D.tris.get(k);
						tri.toMoveX(data3D.max.x);
					}
					char3D.max.x += data3D.max.x;
					data3D.max.x = char3D.max.x;
					data3D.tris.addAll(char3D.tris);
				}
			}
		}
		return data3D;
	}

	/**
	 * 根据参数将字符串转成3D,底部是完全的
	 * 
	 * @param params
	 * @return
	 */
	public static Data3D convert3DFullBottom(List<DrawStringParam> params) {
		int sumLength = 0;
		for (int i = 0; i < params.size(); i++) {
			DrawStringParam param = params.get(i);
			for (int j = 0; j < param.target.length(); j++) {
				sumLength += SIDE_WIDTH * param.scale;
			}
		}

		BufferedImage image = new BufferedImage(sumLength, SIDE_WIDTH, BufferedImage.TYPE_INT_RGB);
		for (int x = 0; x < image.getWidth(); x++) {
			for (int y = 0; y < image.getHeight(); y++) {
				image.setRGB(x, y, Color.WHITE.getRGB());
			}
		}

		Graphics graphics = image.getGraphics();
		// int strWidth = graphics.getFontMetrics().stringWidth("好");
		int baseline = SIDE_WIDTH / 6;
		for (int i = 0; i < params.size(); i++) {
			DrawStringParam param = params.get(i);
			int fontSize = (int) (SIDE_WIDTH * param.scale);
			graphics.setFont(new Font(param.fontName, param.fontStyle, fontSize));
			graphics.setColor(param.color);

			for (int j = 0; j < param.target.length(); j++) {
				graphics.drawString(String.valueOf(param.target.charAt(j)), nextTextBeginDraw(image), SIDE_WIDTH - baseline);
			}
		}
		graphics.dispose();

		BufferedImage grayImg = null;
		grayImg = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_BINARY);// 重点，技巧在这个参数BufferedImage.TYPE_BYTE_BINARY
		for (int i = 0; i < image.getWidth(); i++) {
			for (int j = 0; j < image.getHeight(); j++) {
				int rgb = image.getRGB(i, j);
				grayImg.setRGB(i, j, rgb);
			}
		}
		Data3D data3D = convert3D0(grayImg);
		formalImg3D(data3D, 1f);
		return data3D;
	}

	/**
	 * 计算字的左边位置
	 * 
	 * @param image
	 * @return
	 */
	private static int nextTextBeginDraw(BufferedImage image) {
		int begin = 0;
		int white = Color.WHITE.getRGB();
		for (int x = 0; x < image.getWidth(); x++) {
			for (int y = 0; y < image.getHeight(); y++) {
				if (image.getRGB(x, y) != white) {
					begin = x;
				}
			}
		}
		return begin;
	}

	/**
	 * 长方体
	 *
	 * @param min
	 * @param max
	 * @return
	 */
	public static List<Triangle> cuboid(Point3D min, Point3D max) {
		List<Triangle> cube = new ArrayList<>(6 * 2);
		// 1
		Triangle a = new Triangle();
		a.setFirstPoint(min.x, min.y, min.z);
		a.setSecodePoint(min.x, min.y, max.z);
		a.setThridPoint(min.x, max.y, max.z);

		Triangle b = new Triangle();
		b.setFirstPoint(min.x, min.y, min.z);
		b.setSecodePoint(min.x, max.y, min.z);
		b.setThridPoint(min.x, max.y, max.z);

		// 2
		Triangle c = new Triangle();
		c.setFirstPoint(min.x, min.y, min.z);
		c.setSecodePoint(min.x, max.y, min.z);
		c.setThridPoint(max.x, max.y, min.z);

		Triangle d = new Triangle();
		d.setFirstPoint(min.x, min.y, min.z);
		d.setSecodePoint(max.x, min.y, min.z);
		d.setThridPoint(max.x, max.y, min.z);

		// 3
		Triangle e = new Triangle();
		e.setFirstPoint(min.x, min.y, min.z);
		e.setSecodePoint(min.x, min.y, max.z);
		e.setThridPoint(max.x, min.y, max.z);

		Triangle f = new Triangle();
		f.setFirstPoint(min.x, min.y, min.z);
		f.setSecodePoint(max.x, min.y, min.z);
		f.setThridPoint(max.x, min.y, max.z);

		// 4
		Triangle g = new Triangle();
		g.setFirstPoint(max.x, min.y, min.z);
		g.setSecodePoint(max.x, min.y, max.z);
		g.setThridPoint(max.x, max.y, max.z);

		Triangle h = new Triangle();
		h.setFirstPoint(max.x, min.y, min.z);
		h.setSecodePoint(max.x, max.y, min.z);
		h.setThridPoint(max.x, max.y, max.z);

		// 5
		Triangle i = new Triangle();
		i.setFirstPoint(min.x, max.y, min.z);
		i.setSecodePoint(min.x, max.y, max.z);
		i.setThridPoint(max.x, max.y, max.z);

		Triangle j = new Triangle();
		j.setFirstPoint(min.x, max.y, min.z);
		j.setSecodePoint(max.x, max.y, min.z);
		j.setThridPoint(max.x, max.y, max.z);

		// 6
		Triangle k = new Triangle();
		k.setFirstPoint(min.x, min.y, max.z);
		k.setSecodePoint(min.x, max.y, max.z);
		k.setThridPoint(max.x, max.y, max.z);

		Triangle l = new Triangle();
		l.setFirstPoint(min.x, min.y, max.z);
		l.setSecodePoint(max.x, min.y, max.z);
		l.setThridPoint(max.x, max.y, max.z);

		cube.add(a);
		cube.add(b);

		cube.add(c);
		cube.add(d);

		cube.add(e);
		cube.add(f);

		cube.add(g);
		cube.add(h);

		cube.add(i);
		cube.add(j);

		cube.add(k);
		cube.add(l);

		return cube;
	}

	/**
	 * 生成U盘插头 firstPoint(0,0,0)
	 */
	private static Data3D generateUsbHead() {
		final float LENGTH_GAP = (USB_LENGTH - USB_H_LENGTH) / 2;
		final float HEIGHT_GAP = (USB_HEIGHT - USB_H_HEIGHT) / 2;

		Point3D a = new Point3D(0f, 0f, 0f);// 左侧面左下角
		Point3D b = new Point3D(0f, USB_LENGTH, 0f);// 左侧面左上角
		Point3D c = new Point3D(0f, 0f, USB_HEIGHT);// 左侧面右下角
		Point3D d = new Point3D(0f, USB_LENGTH, USB_HEIGHT);// 左侧面右下角
		Rectangular leftSide = new Rectangular(a, b, c, d);

		Point3D e = new Point3D(USB_WIDTH, 0f, 0f);// 底面
		Point3D g = new Point3D(USB_WIDTH, 0f, USB_HEIGHT);
		Rectangular bottomSide = new Rectangular(a, e, c, g);

		Point3D f = new Point3D(USB_WIDTH, USB_LENGTH, 0f);// 背面
		Rectangular backSide = new Rectangular(a, b, e, f);

		Point3D h = new Point3D(USB_WIDTH, USB_LENGTH, USB_HEIGHT);
		Rectangular topSide = new Rectangular(b, f, d, h);
		Rectangular frontSide = new Rectangular(c, d, g, h);
		// inside w:12.4fh:4f
		Point3D i = new Point3D(USB_WIDTH, LENGTH_GAP, HEIGHT_GAP);// e
		Point3D j = new Point3D(USB_WIDTH, LENGTH_GAP + USB_H_LENGTH, HEIGHT_GAP);// f
		Point3D k = new Point3D(USB_WIDTH, LENGTH_GAP, HEIGHT_GAP + USB_H_HEIGHT);// g
		Point3D l = new Point3D(USB_WIDTH, LENGTH_GAP + USB_H_LENGTH, HEIGHT_GAP + USB_H_HEIGHT);// h

		Point3D m = new Point3D(USB_WIDTH - USB_H_WIDTH, LENGTH_GAP, HEIGHT_GAP);
		Point3D n = new Point3D(USB_WIDTH - USB_H_WIDTH, LENGTH_GAP + USB_H_LENGTH, HEIGHT_GAP);
		Point3D o = new Point3D(USB_WIDTH - USB_H_WIDTH, LENGTH_GAP, HEIGHT_GAP + USB_H_HEIGHT);
		Point3D p = new Point3D(USB_WIDTH - USB_H_WIDTH, LENGTH_GAP + USB_H_LENGTH, HEIGHT_GAP + USB_H_HEIGHT);
		Rectangular iLeftSide = new Rectangular(m, n, o, p);
		Rectangular iBottomSide = new Rectangular(m, i, o, k);
		Rectangular iBackSide = new Rectangular(m, n, i, j);
		Rectangular iTopSide = new Rectangular(n, j, p, l);
		Rectangular iFrontSide = new Rectangular(o, p, k, l);

		// positive side
		// f*r***t*h
		// **j***l**
		// **i***k**
		// e*q***s*g
		Point3D q = new Point3D(USB_WIDTH, 0f, HEIGHT_GAP);// e
		Point3D r = new Point3D(USB_WIDTH, USB_LENGTH, HEIGHT_GAP);// f
		Point3D s = new Point3D(USB_WIDTH, 0f, HEIGHT_GAP + USB_H_HEIGHT);// g
		Point3D t = new Point3D(USB_WIDTH, USB_LENGTH, HEIGHT_GAP + USB_H_HEIGHT);// h
		Rectangular positiveSide1 = new Rectangular(e, f, q, r);
		Rectangular positiveSide2 = new Rectangular(j, r, l, t);
		Rectangular positiveSide3 = new Rectangular(q, i, s, k);
		Rectangular positiveSide4 = new Rectangular(s, t, g, h);

		List<Triangle> triangles = new ArrayList<>();
		triangles.add(leftSide.tri1);// 外框
		triangles.add(leftSide.tri2);
		triangles.add(bottomSide.tri1);
		triangles.add(bottomSide.tri2);
		triangles.add(backSide.tri1);
		triangles.add(backSide.tri2);
		triangles.add(topSide.tri1);
		triangles.add(topSide.tri2);
		triangles.add(frontSide.tri1);
		triangles.add(frontSide.tri2);

		triangles.add(iLeftSide.tri1);// 内框
		triangles.add(iLeftSide.tri2);
		triangles.add(iBottomSide.tri1);
		triangles.add(iBottomSide.tri2);
		triangles.add(iBackSide.tri1);
		triangles.add(iBackSide.tri2);
		triangles.add(iTopSide.tri1);
		triangles.add(iTopSide.tri2);
		triangles.add(iFrontSide.tri1);
		triangles.add(iFrontSide.tri2);

		triangles.add(positiveSide1.tri1);// 正面
		triangles.add(positiveSide1.tri2);
		triangles.add(positiveSide2.tri1);
		triangles.add(positiveSide2.tri2);
		triangles.add(positiveSide3.tri1);
		triangles.add(positiveSide3.tri2);
		triangles.add(positiveSide4.tri1);
		triangles.add(positiveSide4.tri2);

		Data3D usbHead = new Data3D(triangles);
		usbHead.min.set(a.x, a.y, a.z);
		usbHead.max.set(h.x, h.y, h.y);
		return usbHead;
	}

	/**
	 * 生成创意U盘模型3D数据
	 * 
	 * @param word3D
	 * @return
	 */
	public static Data3D multiWord3DUsbHead(Data3D word3D) {
		Data3D usbHead = generateUsbHead();
		for (int i = 0; i < usbHead.tris.size(); i++) {
			Triangle tri = usbHead.tris.get(i);
			tri.toMoveX(word3D.max.x);
		}
		word3D.tris.addAll(usbHead.tris);
		word3D.updateMax(usbHead.max.x, usbHead.max.y, usbHead.max.z);
		return word3D;
	}
}
